#define _GNU_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdlib.h>

#include <string.h>
#include <sys/select.h>

pid_t pty_fork(int *pty_m)
{
    int ptm, pts;
    char *pty; // slave pty name
    pid_t pid;

    ptm = getpt();
    if (ptm < 0) {
        printf("open master pty error\n");
        goto master_error;
    }

    *pty_m = ptm;

    if (grantpt(ptm) < 0 || unlockpt(ptm) < 0) {
        printf("access master pty error\n");
        goto master_error;
    }

    pid = fork();
    if (pid < 0) {
        printf("fork error!\n");
        return -1;
    } else if (pid == 0) { // child
        if (setsid() < 0) {
            printf("setsid error!\n");
            return -1;
        } 

        pty = ptsname(ptm);
        if (pty == NULL) {
            printf("get slave pty name error!\n");
            goto master_error;
        }
        printf("slave pty: %s\n", pty);

        /* slave */
        pts = open(pty, O_RDWR);
        if (pts < 0) {
            printf("open ");
            return -1;
        }

        close(ptm);
        ptm = 0;

        ioctl(pts, TIOCSCTTY, NULL);

        // ioctl(pts, TIOCSCTTY, 0);

        if (dup2(pts, STDIN_FILENO) != STDIN_FILENO) {
            return -1;
        }
        if (dup2(pts, STDOUT_FILENO) != STDOUT_FILENO) {
            return -1;
        }
        if (dup2(pts, STDERR_FILENO) != STDERR_FILENO) {
            return -1;
        }
        if (pts != STDIN_FILENO && pts != STDOUT_FILENO && pts != STDERR_FILENO) {
            close(pts);
            printf("dup2 - done, pts=%d !\n", pts);
        }


        char *argv[] = {"/bin/login", NULL};
        execvp(argv[0], argv);
        printf("maybe\n");
        return 0;
    } else { // parent
        close(pts);
        return pid;
    }

master_error:
    close(ptm);
    return -1;

slave_error:
    return -2;
}

void link_recv()
{
}

void link_send()
{
}

int main()
{
    int ptm = 0;
    int pid = pty_fork(&ptm);
    if (pid < 0) {
        printf("fuckoff\n");
        exit(-1);
    }
    if (pid == 0) {
        printf("this is child!\n");

    }
    printf("pid: %d\n", pid);
    printf("ptm: %d\n", ptm);

    fd_set rfds;
    int retval;

    /* Watch stdin (fd 0) to see when it has input. */

    char buf0[128];
    char buf1[128];

    while (1) {
        FD_ZERO(&rfds);
        FD_SET(0, &rfds);
        FD_SET(ptm, &rfds);

        /* Wait up to five seconds. */

        retval = select(ptm + 1, &rfds, NULL, NULL, NULL);
        /* Don't rely on the value of tv now! */

        if (retval == -1) {
            perror("select()");
            printf("and, ptm: %d\n", ptm);
            return -1;
        } else if (retval) {
            // printf("Data is available now.\n");
            if (FD_ISSET(0, &rfds)) {
                memset(buf0, 0, sizeof(buf0));
                int len = read(0, buf0, sizeof(buf0));
                if (len < 0) {
                    printf("read stdin error!\n");
                    return -1;
                }
                write(ptm, buf0, len);
            }
            if (FD_ISSET(ptm, &rfds)) {
                memset(buf1, 0, sizeof(buf0));
                int len = read(ptm, buf1, sizeof(buf0));
                if (len < 0) {
                    printf("read ptm error!\n");
                    return -1;
                }
                write(1, buf1, len);
            }
        }
        /* FD_ISSET(0, &rfds) will be true. */
        else
            printf("No data within five seconds.\n");
    }
    return 0;
}
